#include "Monsters.hpp"
#include "Utils.hpp"
#include "Clock.hpp"
#include <assert.h>
#include <vector>
#include <iostream>
#include <string>
#include <math.h>
#ifdef LINUX
#include <unistd.h>
#endif

using namespace MonsterFactory;

std::string GenerateName() {
  int nameLength = RandomGenerator(3, 8);
  char capital = RandomGenerator(static_cast<int>('A'), static_cast<int>('Z'));
  std::string name;
  name.push_back(capital);
  for (int i = 0; i < nameLength - 1; ++i)
    name.push_back(RandomGenerator(static_cast<int>('a'), static_cast<int>('z')));

  return name;
}

bool IAnimalMonster::PerformTask() {
  std::string str = name + " lv." + std::to_string(level);
  std::string bar = "===================";
  std::cout << bar << std::endl << str << std::endl << bar << std::endl;
  const int scriptsSize = 8;
  static std::string scripts[scriptsSize] = {
    "GE is a multinational conglomerate headquartered in Boston, Massachusetts.",
    "Its main offices are located at 30 Rockefeller Plaza at Rockefeller Center in New York City.",
    "It was formerly known as the GE Building for the prominent GE logo on the roof.",
    "Through its RCA subsidiary, it has been associated with the center since its construction in the 1930s.",
    "GE moved its corporate headquarters from the GE Building on Lexington Avenue to Fairfield in 1974.", /*30 s for level 15*/
    "The company describes itself as composed of a number of primary business units or \"businesses\".",
    "Each unit is itself a vast enterprise, many of which would, even as a standalone company, rank in the Fortune 500.",
    "The list of GE businesses varies over time as the result of acquisitions, divestitures and reorganizations."
  };

  static const int baseLevel = 1;
  static const double baseTime = 53.13/scripts[4].size();

  int randomNum = RandomGenerator(0, scriptsSize-1);
  double time = baseTime*scripts[randomNum].size()*pow(0.96, level-baseLevel);
  std::cout << "please type in the sentence asap.\n";
  std::cout << scripts[randomNum] << std::endl;
  char chs[200];

  Clock* clock = Clock::GetInstance();
  clock->Tic();
  std::cin.getline(chs, 200, '\n');
  std::string input(chs);
  clock->Toc();

  std::cout << "Time elapsed:  " << clock->GetDuration() << " s" << " Target: " << time << " s" << std::endl;
  if (input == scripts[randomNum] && clock->GetDuration() < time) {
    std::cout << "Win!\n";
    return true;
  } else {
    std::cout << "Lose!\n";    
    return false;
  }
}
bool IPlantMonster::PerformTask() {
  std::string str = name + " lv." + std::to_string(level);
  std::string bar = "===================";  
  std::cout << bar << std::endl << str << std::endl << bar << std::endl;

  static const double baseTime = 0.3;

  int difficulty = ((level / 2) > 9) ? 9 : (level / 2);
  double time = pow(0.98, difficulty+1)*baseTime*(difficulty+1) + 1.0;
  std::string output;
  char sets[] = {'1', '2', '3'};
  for (int i = 0; i < difficulty+1; ++i) {
    int index = RandomGenerator(0,2);
    output.push_back(sets[index]);
  }

  std::cout << "please type in the characters formed by 1, 2, 3 asap once it appears in 3 seconds.\n" << std::endl; 
#ifdef LINUX
	sleep(3);
#else
	Sleep(3000);
#endif
  std::cout << output << std::endl;

  Clock* clock = Clock::GetInstance();
  std::string input;
  std::cin.ignore();
  clock->Tic();
  std::cin >> input;
  clock->Toc();

  std::cout << "Time elapsed:  " << clock->GetDuration() << " s" << " Target: " << time << " s" << std::endl;
  if (input == output && clock->GetDuration() < time) {
    std::cout << "Win!\n";
    return true;
  } else {
    std::cout << "Lose!\n";    
    return false;
  }
}

bool IEliteMonster::PerformTask() {
  std::string str = name + " lv." + std::to_string(level);
  std::string bar = "===================";    
  std::cout << bar << std::endl << str << std::endl << bar << std::endl;

  static const double baseTime = 0.22;

  int difficulty = ((level / 2) > 9) ? 9 : (level / 2);
  double time = pow(0.97, difficulty+1)*baseTime*(difficulty+1) + 1.0;
  std::string output;
  char sets[] = {'1', '2', '3'};
  for (int i = 0; i < difficulty+1; ++i) {
    int index = RandomGenerator(0,2);
    output.push_back(sets[index]);
  }

  std::cout << "please type in the characters formed by i,j,k,l asap once it appears in 3 seconds.\n" << std::endl;
#ifdef LINUX
	sleep(3);
#else
	Sleep(3000);
#endif
  std::cout << output << std::endl;

  Clock* clock = Clock::GetInstance();
  std::string input;
  std::cin.ignore();
  clock->Tic();
  std::cin >> input;
  clock->Toc();

  std::cout << "Time elapsed:  " << clock->GetDuration() << " s" << " Target: " << time << " s" << std::endl;
  if (input == output && clock->GetDuration() < time) {
    std::cout << "Win!\n";
    return true;
  } else {
    std::cout << "Lose!\n";    
    return false;
  }
}


BossMonster::BossMonster(int level) {
  // level should be with [1,20]
  assert(level >=1 && level <= 20);

  this->level = level;

  name = "Boss " + GenerateName();
}
std::string BossMonster::OpeningWords() {
  return name + "  lv." + std::to_string(level);        
}

PigMonster::PigMonster(int level) {
  // level should be with [1,10]
  assert(level >= 1 && level <=10);

  this->level = level;

  name = "Pig " + GenerateName();
}

std::string PigMonster::OpeningWords() {
  return name + "  lv." + std::to_string(level);          
}


HorseMonster::HorseMonster(int level) {
  // level should be with (10,20]
  assert(level > 10 && level <=20);

  this->level = level;

  name = "Horse " + GenerateName();
}

std::string HorseMonster::OpeningWords() {
  return name + "  lv." + std::to_string(level);            
}

TreeMonster::TreeMonster(int level) {
  // level should be with [1,10]
  assert(level >= 1 && level <=10);

  this->level = level;

  name = "Tree " + GenerateName();  
}

std::string TreeMonster::OpeningWords() {
  return name + "  lv." + std::to_string(level);              
}

FlowerMonster::FlowerMonster(int level) {
  // level should be with (10,20]
  assert(level > 10 && level <=20);

  this->level = level;

  name = "Flower " + GenerateName();    
}

std::string FlowerMonster::OpeningWords() {
  return name + "  lv." + std::to_string(level);                
}

